#! lua

--[[ This library exports

  TAG, MONOTAG, BEGIN, END, TEXT, SPACE, REM and link

--]]

--[[
  The rope datatype is defined recursively:

  data rope = string | list of rope.

Use:  status,err = rope.walk(rope,function)
      print(table.concat(err,"."))
Error message prints out index at each level.
]]

-- ropes -------

local t_err = "<bad type: %s>"
local a_err = "<bad arg: %s>"

local walk = \ (x,f)
       local g
       g = \ (x,e)
         local x_type, status = type (x)
         if x_type == "string" then
           status = pcall (f,x)
           if not status then e[1+#e] = a_err:format (x) end -- if
           => status,e
         elseif x_type == "table" then
           e[1+#e] = ""
           for i,y in ipairs (x) do
             e[#e] = tostring (i)
             status = g(y,e)
             if not status then => nil,e end -- if
           end -- for
           e[#e] = nil
           => true
         else
           e[1+#e] = t_err:format (x_type)
           => nil,e
         end -- if
       end -- function
       => g (x, { })
       end

local open in io
local file_err = "cannot open %s"
local outdo = \ (x,file)
      local o = assert (open (file,"w"),file_err:format (file))
      local f = \ (s)
                o:write (s or "")
                => true
                end -- function
      local status,err = walk (x,f)
      o:close ( )
      => status,err
      end -- function

------ markup stuff --------------
--[[
Two types of tag:

  monotag    <foo attributestring >
  tag    <foo attributestring> rope </foo>

where attributestring could be empty.
  ]]

local monotagger = {
__index = \ (_,tag)
          assert (type (tag) == "string")
          => \ (attr)
                 local o = {"<",tag}
                 if attr and (#attr > 0) then
                   o[1+#o] = " "
                   o[1+#o] = attr
                 end -- if
                 o[1+#o] = ">"
                 => o
                 end -- function
           end -- function
          }

local bitagger = {
__index = \ (_,tag)
          assert (type (tag) == "string")
          => \ (attr)
                 => \ (rope)
                  local o = {"<",tag}
                  if attr and (#attr > 0) then
                   o[1+#o] = " "
                   o[1+#o] = attr
                  end -- if
                  o[1+#o] = ">"
                  o[1+#o] = rope
                  o[1+#o] = "</"
                  o[1+#o] = tag
                  o[1+#o] = ">"
                  => o
           end end end
           }
local MONOTAG, TAG = {},{}
setmetatable(MONOTAG,monotagger)
setmetatable(TAG,bitagger)




local doctype = [[
<!DOCTYPE html>]]

local HTML = TAG.html [[]]

local nl = "\n"

local BEGIN = \(filename)
        if filename then
          assert(type(filename) == "string","BEGIN takes a string or nil")
        end -- if
        => { FILE = filename or this.."/html"; }
        end

local META,LINK = MONOTAG.meta,MONOTAG.link
local HEAD,BODY,TITLE = TAG.head "",TAG.body,TAG.title ""
local css = [[rel="stylesheet" type="text/css" media="screen" href="]]

local execute in os
local concat in table
local END = \(rope)
      local file = rope.FILE
      local style = rope.STYLE
      local doc = {
       rope.DOCTYPE or doctype;
       nl;
       HTML {
        nl;
        HEAD {
         nl;
         TITLE (rope.TITLE or "");
         META [[http-equiv="Content-Type" content="text/html; charset=utf-8"]];
         nl;
         META [[name="Generator" content="RiscLua"]];
         nl;
         META [[name="MSSmartTagsPreventParsing" content="TRUE"]];
         nl;
         rope.HEAD or "";
         style and LINK {css;style;'"'} or "";
             };
         nl;
         BODY (rope.ATTR or "") (rope.BODY or "");
         nl;
           };
                   }
       status, err = outdo(doc,file)
       if status then
        execute("settype "..file.." html")
       else
        print(concat(err,'.'))
       end -- if
       end --function

local entity = {
  ["<"] = "&lt;",
  [">"] = "&gt;",
  ["&&"] = "&amp;",
               }
local catchall = "(.)"
local entify = \ (c)
               local n,fmts = c:byte(),"&#%s;"
               => (n > 127) and fmts:format(n) or c
               end -- function

local TEXT = \ (rope)
       local o = { }
       local f = \ (s)
           local status = type (s) == "string"
           if status then
             for symb,ent in pairs (entity) do
               s = s:gsub (symb,ent)
             end -- for
             s = s:gsub (catchall,entify)
             o[1+#o] = s
           end -- if
            => status
          end -- function
       walk (rope,f)
       => o
       end -- function

local SPACE = \ (n)
        assert(type (n) == "number","SPACE takes a number")
        local s = "&nbsp;"
        => s:rep (n)
        end -- function

local REM = \ (s) => { "\n<!-- ";s;" -->\n"; } end -- function

local link = \ (url) => TAG.a { 'href="'; url; '"'; } end -- function
=> {
   TAG = TAG;
   MONOTAG = MONOTAG;
   BEGIN = BEGIN;
   END = END;
   TEXT = TEXT;
   SPACE = SPACE;
   REM = REM;
   link = link;
   }
